import 'dart:convert';

import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:kq_flutter_core_widget/network/cancel_token_group.dart';
import 'package:kq_flutter_core_widget/utils/log.dart';
import 'package:kq_flutter_core_widget/network/response.dart';

import '../resources/l10n/l10n.dart';

class Http {
  static const logTag = "http";

  static const statusOk = 200;
  static const statusError = 202;

  // 创建 Dio 实例
  static final Dio _dio = Dio(_getDefOptions())
    ..interceptors.add(_GlobalParamInterceptor());

  // 记录未分组的CancelToken
  static const _defaultCancelTokenGroup = "defaultCancelTokenGroup";

  // 记录所有请求用于取消请求的标识
  static final Map<String, List<CancelToken>> _cancelTokenList = {};

  static BaseOptions _getDefOptions() {
    BaseOptions options = BaseOptions();
    options.connectTimeout = const Duration(milliseconds: 10 * 1000);
    options.receiveTimeout = const Duration(milliseconds: 20 * 1000);
    return options;
  }

  /// 执行http请求
  ///
  /// 当请求被取消时，将抛出错误类型为[DioErrorType.cancel]的[DioError]
  static Future<ApiResponse<T>> _request<T>(
      String path, String method, T Function(dynamic json)? fromJson,
      {Map<String, dynamic>? queryParameters,
      Map<String, dynamic>? data,
      Map<String, dynamic>? headers,
      CancelToken? cancelToken}) async {
    var finalCancelToken = cancelToken ?? CancelTokenEx.get();
    _addCancelToken(finalCancelToken);
    try {
      Response response = await _dio.request(path,
          queryParameters: queryParameters,
          data: data,
          cancelToken: cancelToken,
          options: Options(method: method, headers: headers));
      Log.d(response, tag: logTag);
      if (response.statusCode != statusOk &&
          response.statusCode != statusError) {
        return ApiResponse(
            code: ApiResponse.failed, msg: mapHttpError(response.statusCode));
      }

      var mapData = mapResponseData(response.data);
      if (mapData == null) {
        return ApiResponse(
            code: ApiResponse.failed, msg: mapDioErrorMsg(DioErrorType.unknown));
      }
      return ApiResponse.fromJson(mapData, fromJson);
    } on DioError catch (e) {
      if (e.type == DioErrorType.cancel) {
        // 请求被取消
        rethrow;
      }

      Log.e(e, tag: logTag);
      return ApiResponse(code: ApiResponse.failed, msg: mapDioErrorMsg(e.type));
    } catch (e) {
      Log.e(e, tag: logTag);
      return ApiResponse(
          code: ApiResponse.failed, msg: mapDioErrorMsg(DioErrorType.unknown));
    } finally {
      _removeCancelToken(finalCancelToken);
    }
  }

  static void _addCancelToken(CancelToken? cancelToken) {
    if (cancelToken == null) {
      return;
    }

    var group = cancelToken.groupName;
    List<CancelToken>? list;
    if (group == null) {
      list = _cancelTokenList[_defaultCancelTokenGroup];
      if (list == null) {
        list = [];
        _cancelTokenList[_defaultCancelTokenGroup] = list;
      }
    } else {
      list = _cancelTokenList[group];
      if (list == null) {
        list = [];
        _cancelTokenList[group] = list;
      }
    }
    list.add(cancelToken);
  }

  static void _removeCancelToken(CancelToken? cancelToken) {
    if (cancelToken == null) {
      return;
    }

    var group = cancelToken.groupName;
    if (group == null) {
      var list = _cancelTokenList[_defaultCancelTokenGroup];
      if (list != null) {
        list.remove(cancelToken);
      }
    } else {
      var list = _cancelTokenList[group];
      if (list != null) {
        list.remove(cancelToken);
      }
    }
  }

  /// 根据返回的数据，转化为Map对象
  static Map<String, dynamic>? mapResponseData(dynamic data) {
    try {
      if (data is Map<String, dynamic>) {
        return data;
      }

      if (data is String) {
        return json.decode(data);
      }
    } catch (e) {
      return null;
    }

    return null;
  }

  /// 根据[DioErrorType]返回对应错误消息
  static String mapDioErrorMsg(DioErrorType type) {
    switch (type) {
      case DioErrorType.connectionTimeout:
      case DioErrorType.sendTimeout:
        return S.current.connect_time_out_hint;
      case DioErrorType.receiveTimeout:
        return S.current.response_time_out_hint;
      default:
        return S.current.response_error_hint;
    }
  }

  /// 处理 Http 错误码,返回对应的错误消息
  static String mapHttpError(errorCode) {
    String message;
    switch (errorCode) {
      case 400:
        message = S.current.http_400_error;
        break;
      case 401:
        message = S.current.http_401_error;
        break;
      case 403:
        message = S.current.http_403_error;
        break;
      case 404:
        message = S.current.http_404_error;
        break;
      case 408:
        message = S.current.http_408_error;
        break;
      case 500:
        message = S.current.http_500_error;
        break;
      case 501:
        message = S.current.http_501_error;
        break;
      case 502:
        message = S.current.http_502_error;
        break;
      case 503:
        message = S.current.http_503_error;
        break;
      case 504:
        message = S.current.http_504_error;
        break;
      case 505:
        message = S.current.http_505_error;
        break;
      default:
        message = '${S.current.http_other_error}$errorCode';
    }
    return message;
  }

  /// GET请求
  ///
  /// 请求[uri]接口，[headers]指定请求头，[queryParameters]指定请求参数。
  ///
  /// 通过[fromJson]对接口返回的数据进行反序列化，得到泛型[T]类对象。
  ///
  /// 如果需要取消接口，可设置[cancelToken]，想要批量取消请求，[cancelToken]可设置为[CancelTokenGroup],
  /// 当请求被取消时，将抛出错误类型为[DioErrorType.cancel]的[DioError]。
  static Future<ApiResponse<T>> get<T>(String uri,
      {required T Function(dynamic json) fromJson,
      Map<String, dynamic>? queryParameters,
      Map<String, dynamic>? headers,
      CancelToken? cancelToken}) {
    return _request(uri, "GET", fromJson,
        queryParameters: queryParameters,
        headers: headers,
        cancelToken: cancelToken);
  }

  /// POST请求
  ///
  /// 请求[uri]接口，[headers]指定请求头，通过[data]传递数据到后台。
  ///
  /// 如果需要取消接口，可设置[cancelToken]，想要批量取消请求，[cancelToken]可设置为[CancelTokenGroup],
  /// 当请求被取消时，将抛出错误类型为[DioErrorType.cancel]的[DioError]。
  ///
  /// 如果关心返回的data数据，则传递对应实体的[fromJson]方法即可
  static Future<ApiResponse<T>> post<T>(String path,
      {Map<String, dynamic>? data,
      Map<String, dynamic>? headers,
      CancelToken? cancelToken,
      T Function(dynamic json)? fromJson}) {
    return _request(path, "POST", fromJson,
        data: data, headers: headers, cancelToken: cancelToken);
  }

  /// 关闭所有请求
  static void closeAll() {
    for (var element in _cancelTokenList.values) {
      for (var value in element) {
        if (!value.isCancelled) {
          value.cancel("close all");
        }
      }
    }
    _cancelTokenList.clear();
  }

  /// 关闭指定分组请求
  static void closeByGroup(String groupName) {
    var list = _cancelTokenList[groupName];
    if (list == null || list.isEmpty) {
      return;
    }

    for (var value in list) {
      if (!value.isCancelled) {
        value.cancel("close group:$groupName");
      }
    }
    _cancelTokenList.remove(groupName);
  }
}

/// 全局请求参数
class _GlobalParamInterceptor extends InterceptorsWrapper {
  static const String _tokenKey = "key";
  static const String _languageKey = "language";
  static const String _platformKey = "type";
  static const String _systemType = "systemType";

  _GlobalParamInterceptor();

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    if (options.method == "GET") {
      options.queryParameters.addAll(_getGlobalParams());
    } else if (options.data == null) {
      options.data = _getGlobalParams();
    } else if (options.data is Map<String, dynamic>) {
      var map = options.data as Map<String, dynamic>;
      map.addAll(_getGlobalParams());
    }

    _printLog(options);
    super.onRequest(options, handler);
  }

  void _printLog(RequestOptions options) {
    if (!kDebugMode) {
      return;
    }

    if (options.method == "GET") {
      var params = "?";
      options.queryParameters.forEach((key, value) {
        if (value != null) {
          if (params.length > 1) {
            params = "$params&$key=$value";
          } else {
            params = "$params$key=$value";
          }
        }
      });
      Log.d(
          "${options.method}:${options.path}${params.length > 1 ? params : ''}",
          tag: "http");
    } else {
      Log.d("${options.method}:${options.path}", tag: "http");
    }

    Log.d("headers:${options.headers}", tag: "http");
    if (options.data != null) {
      Log.d("body:${options.data}", tag: "http");
    }
  }

  Map<String, String> _getGlobalParams() {
    return {
      _languageKey: "ch",
      _platformKey: "PDA",
      _systemType: "ASSEMBLY",
    };
  }
}
